package com.caishi.lkx.user.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.caishi.lkx.common.role.Role;
import com.caishi.lkx.user.mapper.ActionRoleMapper;
import com.caishi.lkx.user.model.ActionModel;
import com.caishi.lkx.user.model.ActionRoleModel;
import com.caishi.lkx.user.model.UserModel;
import com.caishi.lkx.user.model.re.ActionRoleReModel;
import com.caishi.lkx.user.model.re.UserRoleReModel;
import com.caishi.lkx.user.service.IActionRoleService;
import com.caishi.lkx.user.service.IActionService;
import com.caishi.lkx.user.service.IUserService;
import com.zzw.common.Wrappers;
import com.zzw.common.entity.IBaseMapper;
import com.zzw.common.entity.impl.BaseEntity;
import com.zzw.common.utils.TimeUtil;

import com.caishi.lkx.user.mapper.re.ActionRoleReMapper;
import com.caishi.lkx.user.mapper.re.UserRoleReMapper;

import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

//import org.springframework.stereotype.Service;

/**
 * @author by keray
 * date:2021/4/13 9:40 上午
 */
@Service
@Primary
//@DubboService(interfaceClass = IActionRoleBaseService.class)
public class ActionRoleServiceImpl implements IActionRoleService {

    @Resource
    private ActionRoleMapper actionRoleMapper;

    @Resource
    private ActionRoleReMapper actionRoleReMapper;


    @Resource
    private UserRoleReMapper userRoleReMapper;

    @Resource
    private IUserService<UserModel> userService;

    @Resource
    private IActionService actionService;

    @Override
    public IBaseMapper<ActionRoleModel, String> getMapper() {
        return actionRoleMapper;
    }


    @Override
    public void addAction(String roleCode, List<String> actionCodes) {
        List<String> oldIds = actionRoleReMapper.selectList(Wrappers.lambdaQuery(new ActionRoleReModel())
                .eq(ActionRoleReModel::getRoleName, roleCode)
        ).stream().map(ActionRoleReModel::getActionCode).toList();
        List<String> delIds = oldIds.stream().filter(id -> !actionCodes.contains(id)).collect(Collectors.toList());
        List<String> addIds = actionCodes.stream().filter(id -> !oldIds.contains(id)).collect(Collectors.toList());
        if (CollectionUtil.isNotEmpty(delIds)) {
            removeActionCodes(roleCode, delIds);
        }
        if (CollectionUtil.isNotEmpty(addIds)) {
            addActionCodes(roleCode, addIds);
        }
    }


    @Override
    public List<ActionModel> roleAction(String roleCode) {
        return actionService.selectList(Wrappers.lambdaQuery(new ActionModel())
                .in(ActionModel::getId, roleActionCodes(roleCode))
        );
    }


    @Override
    public Integer roleHaveActionCount(String roleCode) {
        Long res = actionRoleReMapper.selectCount(Wrappers.lambdaQuery(new ActionRoleReModel())
                .eq(ActionRoleReModel::getRoleName, roleCode)
        );
        return res == null ? null : res.intValue();
    }

    @Override
    public List<String> roleActionCodes(String roleCode) {
        return actionRoleReMapper.selectList(Wrappers.lambdaQuery(new ActionRoleReModel())
                .eq(ActionRoleReModel::getRoleName, roleCode)
        ).stream().map(ActionRoleReModel::getActionCode).collect(Collectors.toList());
    }


    @Transactional(rollbackFor = Exception.class)
    @Override
    public void userSettingRoles(String userId, List<UserRoleReModel> roles) {
        List<String> oldIds = userRoleReMapper.selectList(Wrappers.lambdaQuery(new UserRoleReModel())
                .select(UserRoleReModel::getRoleCode)
                .eq(UserRoleReModel::getUserId, userId)
        ).stream().map(UserRoleReModel::getRoleCode).collect(Collectors.toList());
        List<String> newIds = roles.stream().map(UserRoleReModel::getRoleCode).collect(Collectors.toList());
        List<String> delIds = oldIds.stream().filter(id -> !newIds.contains(id)).collect(Collectors.toList());
        List<String> addIds = newIds.stream().filter(id -> !oldIds.contains(id)).collect(Collectors.toList());
        List<String> upIds = newIds.stream().filter(oldIds::contains).collect(Collectors.toList());
        if (CollectionUtil.isNotEmpty(delIds)) {
            removeUserRole(userId, delIds);
        }
        if (CollectionUtil.isNotEmpty(addIds)) {
            addUserRole(userId, roles.stream().filter(v -> addIds.contains(v.getRoleCode())).collect(Collectors.toList()));
        }
        if (CollectionUtil.isNotEmpty(upIds)) {
            upUserRole(userId, roles.stream().filter(v -> upIds.contains(v.getRoleCode())).collect(Collectors.toList()));
        }
    }

    @Override
    public void userSettingRolesByIds(String userId, List<String> roleIds) {
        owner(IActionRoleService.class).userSettingRoles(userId,
                roleIds.stream().map(id -> UserRoleReModel.builder()
                        .userId(userId)
                        .roleCode(id)
                        .expireTime(TimeUtil.MAX)
                        .build()).collect(Collectors.toList())
        );
    }

    @Override
    public String apiRoleName(String userId) {
        return userRoleReMapper.selectList(Wrappers.lambdaQuery(new UserRoleReModel())
                        .select(UserRoleReModel::getRoleCode)
                        .eq(UserRoleReModel::getUserId, userId)
                        .gt(UserRoleReModel::getExpireTime, LocalDateTime.now())
                ).stream()
                .map(UserRoleReModel::getRoleCode)
                .collect(Collectors.joining(","));
    }

    @Override
    public void trantest() {
        actionRoleMapper.update(null, Wrappers.<ActionRoleModel>lambdaUpdate()
                .set(ActionRoleModel::getModifyTime, LocalDateTime.now())
                .eq(BaseEntity::getId, "agent")
        );
    }

    @Override
    public ActionRoleModel code2Role(String code) {
        try {
            Role role = Role.valueOf(code);
            ActionRoleModel model = ActionRoleModel.builder()
                    .name(role.getDesc())
                    .system(true)
                    .build();
            model.setId(code);
            return model;
        } catch (Exception ignore) {
        }
        return actionRoleMapper.selectById(code);
    }

    @Override
    public List<ActionRoleModel> selectUserRoles(String userId) {
        return Arrays.stream(userService.getUserRoleName(userId).split(","))
                .map(v -> owner(IActionRoleService.class).code2Role(v))
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
    }

    @Override
    public int selectUserMaxRoleLevel(String userId) {
        return Arrays.stream(userService.getUserRoleName(userId).split(",")).map(v -> {
            try {
                return Role.valueOf(v).getLevel();
            } catch (Exception e) {
                return 0;
            }
        }).max(Integer::compare).orElse(0);
    }

    @Override
    public List<ActionModel> selectUserAction(String userId) {
        return actionService.selectList(Wrappers.lambdaQuery(new ActionModel())
                .in(ActionModel::getId, selectUserActionCode(userId))
        );
    }

    @Override
    public List<String> selectUserActionCode(String userId) {
//        角色可能以,分隔开
        String apiRoleName = userService.getUserRoleName(userId);
        List<String> roles = Arrays.asList(apiRoleName.split(","));
        return actionRoleReMapper.selectList(Wrappers.lambdaQuery(new ActionRoleReModel()).in(ActionRoleReModel::getRoleName, roles))
                .stream()
                .map(ActionRoleReModel::getActionCode)
                .collect(Collectors.toList());
    }

    private void addActionCodes(String roleCode, List<String> actionCodes) {
        actionRoleReMapper.insertBatch(actionCodes.stream().map(actionCode -> ActionRoleReModel.builder()
                .actionCode(actionCode)
                .roleName(roleCode)
                .build()).collect(Collectors.toList()));
    }

    private void removeActionCodes(String roleCode, List<String> actionCodes) {
        actionRoleReMapper.delete(Wrappers.lambdaUpdate(new ActionRoleReModel())
                .eq(ActionRoleReModel::getRoleName, roleCode)
                .in(ActionRoleReModel::getActionCode, actionCodes)
        );
    }

    private void addUserRole(String userId, List<UserRoleReModel> data) {
        data.forEach(v -> v.setUserId(userId));
        userRoleReMapper.insertBatch(data);
    }

    private void upUserRole(String userId, List<UserRoleReModel> data) {
        data.forEach(v -> {
            if (v.getExpireTime() != null) {
                userRoleReMapper.update(null, Wrappers.lambdaUpdate(new UserRoleReModel())
                        .set(UserRoleReModel::getExpireTime, v.getExpireTime())
                        .eq(UserRoleReModel::getUserId, userId)
                        .eq(UserRoleReModel::getRoleCode, v.getRoleCode())
                        .ne(UserRoleReModel::getExpireTime, v.getExpireTime())
                );
            }
        });
    }

    private void removeUserRole(String userId, List<String> delIds) {
        userRoleReMapper.delete(Wrappers.<UserRoleReModel>lambdaUpdate()
                .eq(UserRoleReModel::getUserId, userId)
                .in(UserRoleReModel::getRoleCode, delIds)
        );
    }

    @Override
    public List<ActionRoleModel> selectList(Wrapper<ActionRoleModel> queryWrapper) {
        return IActionRoleService.super.selectList(queryWrapper);
    }

//    @Override
//    public String rpcApiRoleName(String userId) {
//        return userRoleReMapper.selectList(Wrappers.lambdaQuery(new UserRoleReModel())
//                        .select(UserRoleReModel::getRoleCode)
//                        .eq(UserRoleReModel::getUserId, userId)
//                        .gt(UserRoleReModel::getExpireTime, LocalDateTime.now())
//                ).stream()
//                .map(UserRoleReModel::getRoleCode)
//                .collect(Collectors.joining(","));
//    }
}
